#include <QtCore/QMetaObject>
#include <QtGui/QApplication>
#include <QtGui/QPushButton>
#include <QtGui/QVBoxLayout>
#include <Box2D/Box2D.h>
#include <QyGLViewWidget.h>
#include <QyAbstractRender.h>
#include <QyGLInputController.h>
#include <QyToolRender.h>
#include <QyDrawWallTool.h>
#include "Render.h"

const float32 timeStep = 1.0f / 60.0f;
const int32 velocityIterations = 6;
const int32 positionIterations = 2;

static QyMath::Vector toMathVec( const b2Vec2 & v ) {

    return QyMath::Vector( v.x, v.y );
}

class TestWorld : public QObject, public QyToolRender, public QyGL::InputController {
    Q_OBJECT
public:
    TestWorld( QyGL::ViewWidget * widget )
        : QyGL::InputController(widget), world( b2Vec2( 0.0, 0.0 ), true ), keyController(widget), p_widget(widget)
    {

        uint32 flags =
            b2DebugDraw::e_shapeBit
//             |b2DebugDraw::e_jointBit
//             |b2DebugDraw::e_aabbBit
//             |b2DebugDraw::e_pairBit
//             |b2DebugDraw::e_centerOfMassBit
        ;

        debugPhysicsRender.SetFlags(flags);
        world.SetDebugDraw(&debugPhysicsRender);

        targetBody = makeTestBody( 5, 5, 0.5, 0.5 );
        setEnabled(true);

    }
	virtual void initialize( QyGL::ViewWidget * widget ) {

        widget->qglClearColor(Qt::white);

    }
// 	virtual void resize( int, int ) {}
	void paint( QyGL::ViewWidget * widget ) {

        glPointSize(5);
        glBegin(GL_POINTS);
        glColor3d( 1, 0, 0 );
        glVertex3d( 0, 0, 0 );
        glColor3d( 1, 1, 0 );
        glVertex3dv(widget->glCursor().p);
        glEnd();
        glPointSize(1);

        world.Step(timeStep, velocityIterations, positionIterations);
        world.DrawDebugData();
        QyToolRender::paint(widget);
        processKeys();

    }
    b2Body * makeTestBody( qreal x, qreal y, qreal w, qreal h, qreal dumping = 3.0, qreal friction = 0.3 ) {

        // Define the dynamic body. We set its position and call the body factory.
        b2BodyDef bodyDef;
        bodyDef.type = b2_dynamicBody;
        bodyDef.position.Set( x, y );
//         bodyDef.inertiaScale = 0;
        bodyDef.linearDamping = dumping;
        bodyDef.angularDamping = dumping;
        b2Body* body = world.CreateBody(&bodyDef);

        // Define another box shape for our dynamic body.
        b2PolygonShape dynamicBox;
        dynamicBox.SetAsBox( w, h );

        // Define the dynamic body fixture.
        b2FixtureDef fixtureDef;
        fixtureDef.shape = &dynamicBox;
        fixtureDef.density = 1.0; // Set the box density to be non-zero, so it will be dynamic.
        fixtureDef.friction = friction; // Override the default friction.

        body->CreateFixture(&fixtureDef); // Add the shape to the body.

        return body;
    }
    void processKeys() {

        if ( keyController.match(Qt::Key_W) )
            targetBody->ApplyForce( b2Vec2( 0, 100 ), targetBody->GetPosition() );

        if ( keyController.match(Qt::Key_S) )
            targetBody->ApplyForce( b2Vec2( 0, -100 ), targetBody->GetPosition() );

        if ( keyController.match(Qt::Key_A) )
            targetBody->ApplyForce( b2Vec2( -100, 0 ), targetBody->GetPosition() );

        if ( keyController.match(Qt::Key_D) )
            targetBody->ApplyForce( b2Vec2( 100, 0 ), targetBody->GetPosition() );

    }
	virtual void keyPressd( QKeyEvent * event ) {

        QyMath::Vector pos = p_widget->glCursor();

        switch ( event->key() ) {

        case Qt::Key_Plus:
            makeTestBody( pos.x(), pos.y(), 1, 1 );
            break;

        }

    }

    b2World world;
    DebugDraw debugPhysicsRender;
    b2Body * targetBody;
    QyGL::BaseKeyController keyController;
    QyGL::ViewWidget * p_widget;

public slots:
    void createWall( QVector<QyMath::Vector> geometry, const QyMath::Vector & center ) {

        b2BodyDef wallBodyDef;
        b2PolygonShape wallShape;

        wallBodyDef.position.Set( center.p[0], center.p[1] );
//         wallShape.SetAsBox( 50.0, 0.2 );

        b2Vec2 geo[4];
        geo[0] = b2Vec2( geometry[3].x(), geometry[3].y() );
        geo[1] = b2Vec2( geometry[2].x(), geometry[2].y() );
        geo[2] = b2Vec2( geometry[1].x(), geometry[1].y() );
        geo[3] = b2Vec2( geometry[0].x(), geometry[0].y() );
        wallShape.Set( geo, 4 );

        b2Body * wallBody = world.CreateBody(&wallBodyDef);
        wallBody->CreateFixture( &wallShape, 0.0 );

    }
    void shoot( const QyMath::Vector & pos ) {

        static int bullets = 0;

        QyMath::Vector targetPos = toMathVec( targetBody->GetPosition() );
        qreal direction = targetPos.zDirection(pos);

        for ( direction = 0; direction < 360; direction += 5.0 ) {

            QyMath::Vector bulletPos = QyMath::Vector::move( targetPos, 1.0, direction );
            QyMath::Vector impulse = bulletPos - targetPos;
            impulse *= 60;
            b2Body * bullet = makeTestBody( bulletPos.p[0], bulletPos.p[1], 0.1, 0.1, 0.0, 10 );
            bullet->SetBullet(true);
            bullet->ApplyLinearImpulse( b2Vec2( impulse.p[0], impulse.p[1] ),
                b2Vec2( bulletPos.p[0], bulletPos.p[1] ) );
            bullets ++;

        }

        QyDbgValue(bullets);

    }

};

class QyShootController : public QObject, public QyGL::InputController, public QyAbstractTool {
    Q_OBJECT
public:
    explicit QyShootController( QyGL::ViewWidget * target )
        : QyGL::InputController(target), QyAbstractTool(), timerId(0) {}
    virtual ~QyShootController() {}

    bool isEnabled() const { return QyGL::InputController::isEnabled(); }

public slots:
    void setEnabled( bool enabled ) { QyGL::InputController::setEnabled(enabled); }

signals:
    void shoot( const QyMath::Vector & pos );

private:
    void mousePressd( const QyGL::MouseEvent & event ) {

        p_pos = event.glPos();
        if ( pressButtons() == Qt::LeftButton )
            timerId = startTimer(100);

    }
    void mouseMoved( const QyGL::MouseEvent & event ) {

        if ( pressButtons() == Qt::LeftButton )
            p_pos = event.glPos();

    }
    void mouseReleased( const QyGL::MouseEvent & event ) {

//         if ( pressButtons() == Qt::LeftButton ) {

            p_pos = event.glPos();
            killTimer(timerId);
            timerId = 0;

//         }

    }
    void timerEvent( QTimerEvent * event ) {

        if ( event->timerId() == timerId )
            emit shoot(p_pos);

    }

    QyMath::Vector p_pos;
    int timerId;


};

class ToolsWindow : public QWidget {
    Q_OBJECT
public:
    ToolsWindow( TestWorld * testWorld, QyGL::ViewWidget * viewWidget )
        : drawWallTool( testWorld, viewWidget ), shootController(viewWidget), lastTool(0)
    {

        drawWallTool.setEnabled(false);
        shootController.setEnabled(false);
        testWorld->connect( &drawWallTool,
            SIGNAL( createWall(QVector<QyMath::Vector>,const QyMath::Vector &) ),
            SLOT( createWall(QVector<QyMath::Vector>,const QyMath::Vector &) )
        );
        testWorld->connect( &shootController,
            SIGNAL( shoot(const QyMath::Vector &) ),
            SLOT( shoot(const QyMath::Vector &) )
        );
        on_drawWallButton_clicked();

        QVBoxLayout * lo = new QVBoxLayout(this);
        QPushButton * drawWallButton = new QPushButton( "Draw wall", this );
        QPushButton * shootButton = new QPushButton( "Shoot", this );

        drawWallButton->setObjectName("drawWallButton");
        shootButton->setObjectName("shootButton");
        lo->addWidget(drawWallButton);
        lo->addWidget(shootButton);
        QMetaObject::connectSlotsByName(this);

    }

private slots:
    void on_drawWallButton_clicked() {

        if (lastTool)
            lastTool->setEnabled(false);

        drawWallTool.setEnabled(true);
        lastTool = &drawWallTool;
        setWindowTitle("Draw wall");

    }
    void on_shootButton_clicked() {

        if (lastTool)
            lastTool->setEnabled(false);

        shootController.setEnabled(true);
        lastTool = &shootController;
        setWindowTitle("Shooting");

    }

private:
    QyDrawWallTool drawWallTool;
    QyShootController shootController;
    QyAbstractTool * lastTool;

};

#include "main.moc"

int main( int argCount, char ** argValues ) {

    QApplication app( argCount, argValues );
    QyGL::ViewSettings viewSettings;
    viewSettings.updateRate = 1000/60;
    QyGL::ViewWidget viewWidget(viewSettings);
    TestWorld testWorld(&viewWidget);
    ( new QyGL::BasicCamViewController(&viewWidget) )->setEnabled(true);
    ToolsWindow tools( &testWorld, &viewWidget );

    viewWidget.setRender(&testWorld);
    viewWidget.show();
    tools.show();

    return app.exec();
}
